#!/bin/bash
###############################################################################
#       脚本名称: utils_validate.sh
#       功能描述: 参数与数据校验函数库(非交互式)，提供通用的、非领域特定的校验能力。
#       设计原则：当工具类有独立出来脚本时，需要把对应的校验函数也同时提取到对应的脚本模块中。
#       作者: 林再来
#       日期: 2025-10-20
#       版本: 1.0
#       功能:
#           1、必填参数非空校验
#           2、基础数据类型校验
#              数字校验、整数校验、浮点数校验、布尔值校验、字符串校验、数组校验、对象校验
#           3、特殊格式校验
#              邮箱地址校验、URL地址校验、IP地址校验、MAC地址校验、电话号码校验、日期格式校验、时间格式校验
#           4、配置内容校验
#              必填字段校验、字段类型校验、字段值范围校验、一致性校验、依赖关系校验
#           5、安全策略校验
#              密码强度校验
#       依赖(插件/命令):
#           1、bash
###############################################################################

###############################################################################
# 通用数据校验函数
###############################################################################

# 防止重复加载
if [[ -n "${utils_validate_sh_loaded:-}" ]]; then
    return 0
fi
readonly utils_validate_sh_loaded=1

# 注意：不使用严格模式，避免影响交互式 shell

# 获取脚本目录和项目根目录（如果尚未定义）
if [[ -z "${script_dir:-}" ]]; then
    script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
    readonly script_dir
fi

if [[ -z "${project_root:-}" ]]; then
    project_root="$(cd "${script_dir}/../.." && pwd)"
    readonly project_root
fi

###############################################################################
# 全局常量
###############################################################################

# 数据类型常量
readonly UTILS_VALIDATE_TYPE_INTEGER="integer"
readonly UTILS_VALIDATE_TYPE_FLOAT="float"
readonly UTILS_VALIDATE_TYPE_BOOLEAN="boolean"
readonly UTILS_VALIDATE_TYPE_STRING="string"
readonly UTILS_VALIDATE_TYPE_ARRAY="array"
readonly UTILS_VALIDATE_TYPE_OBJECT="object"

# 错误码常量
readonly UTILS_VALIDATE_ERROR_REQUIRED=64
readonly UTILS_VALIDATE_ERROR_TYPE=65
readonly UTILS_VALIDATE_ERROR_FORMAT=66
readonly UTILS_VALIDATE_ERROR_RANGE=67
readonly UTILS_VALIDATE_ERROR_DEPENDENCY=68

###############################################################################
# 基础校验函数
###############################################################################

# 函数: 校验参数非空
# 参数: $1 - 参数值; $2 - 参数名称(用于错误信息)
# 返回: 0-通过, 64-参数错误
# 说明: 这是一个纯粹的通用校验函数，适用于任何需要非空校验的场景
function utils_validate_required() {
    local value="$1"
    local name="${2:-parameter}"

    if [[ -z "$value" ]]; then
        printf "%s\n" "错误: 必填参数为空: ${name}" >&2
        return $UTILS_VALIDATE_ERROR_REQUIRED
    fi
    return 0
}

###############################################################################
# 基础数据类型校验函数
###############################################################################

# 函数: 校验整数
# 参数: $1 - 输入值; $2 - 参数名称(可选)
# 返回: 0-通过, 65-类型错误
# 说明: 检查输入值是否为有效整数
function utils_validate_integer() {
    local value="$1"
    local name="${2:-参数}"
    
    if [[ -z "$value" ]]; then
        printf "%s\n" "错误: ${name}不能为空" >&2
        return $UTILS_VALIDATE_ERROR_REQUIRED
    fi
    
    if [[ ! "$value" =~ ^-?[0-9]+$ ]]; then
        printf "%s\n" "错误: ${name}不是有效的整数: ${value}" >&2
        return $UTILS_VALIDATE_ERROR_TYPE
    fi
    
    return 0
}

# 函数: 校验浮点数
# 参数: $1 - 输入值; $2 - 参数名称(可选)
# 返回: 0-通过, 65-类型错误
# 说明: 检查输入值是否为有效浮点数
function utils_validate_float() {
    local value="$1"
    local name="${2:-参数}"
    
    if [[ -z "$value" ]]; then
        printf "%s\n" "错误: ${name}不能为空" >&2
        return $UTILS_VALIDATE_ERROR_REQUIRED
    fi
    
    if [[ ! "$value" =~ ^-?[0-9]+(\.[0-9]+)?$ ]]; then
        printf "%s\n" "错误: ${name}不是有效的浮点数: ${value}" >&2
        return $UTILS_VALIDATE_ERROR_TYPE
    fi
    
    return 0
}

# 函数: 校验布尔值
# 参数: $1 - 输入值; $2 - 参数名称(可选)
# 返回: 0-通过, 65-类型错误
# 说明: 检查输入值是否为有效布尔值(true/false/1/0/yes/no)
function utils_validate_boolean() {
    local value="$1"
    local name="${2:-参数}"
    
    if [[ -z "$value" ]]; then
        printf "%s\n" "错误: ${name}不能为空" >&2
        return $UTILS_VALIDATE_ERROR_REQUIRED
    fi
    
    case "$value" in
        true|false|1|0|yes|no|TRUE|FALSE|YES|NO|True|False|Yes|No)
            return 0
            ;;
        *)
            printf "%s\n" "错误: ${name}不是有效的布尔值: ${value}" >&2
            return $UTILS_VALIDATE_ERROR_TYPE
            ;;
    esac
}

# 函数: 校验字符串
# 参数: $1 - 输入值; $2 - 最小长度(可选); $3 - 最大长度(可选); $4 - 参数名称(可选)
# 返回: 0-通过, 65-类型错误, 67-范围错误
# 说明: 检查输入值是否为有效字符串，并可选择长度范围
function utils_validate_string() {
    local value="$1"
    local min_length="${2:-0}"
    local max_length="${3:-}"
    local name="${4:-参数}"
    
    if [[ -z "$value" ]]; then
        printf "%s\n" "错误: ${name}不能为空" >&2
        return $UTILS_VALIDATE_ERROR_REQUIRED
    fi
    
    local length=${#value}
    
    if [[ $length -lt $min_length ]]; then
        printf "%s\n" "错误: ${name}长度不能小于${min_length}个字符，当前长度: ${length}" >&2
        return $UTILS_VALIDATE_ERROR_RANGE
    fi
    
    if [[ -n "$max_length" && $length -gt $max_length ]]; then
        printf "%s\n" "错误: ${name}长度不能大于${max_length}个字符，当前长度: ${length}" >&2
        return $UTILS_VALIDATE_ERROR_RANGE
    fi
    
    return 0
}

# 函数: 校验数组
# 参数: $1 - 输入值(以空格分隔的字符串); $2 - 参数名称(可选)
# 返回: 0-通过, 65-类型错误
# 说明: 检查输入值是否为有效数组格式
function utils_validate_array() {
    local value="$1"
    local name="${2:-参数}"
    
    if [[ -z "$value" ]]; then
        printf "%s\n" "错误: ${name}不能为空" >&2
        return $UTILS_VALIDATE_ERROR_REQUIRED
    fi
    
    # 检查是否为有效的数组格式（以空格分隔的字符串）
    if [[ "$value" =~ ^[^[:space:]]+([[:space:]]+[^[:space:]]+)*$ ]] || [[ "$value" =~ ^\[.*\]$ ]]; then
        return 0
    else
        printf "%s\n" "错误: ${name}不是有效的数组格式: ${value}" >&2
        return $UTILS_VALIDATE_ERROR_TYPE
    fi
}

# 函数: 校验对象
# 参数: $1 - 输入值(JSON格式字符串); $2 - 参数名称(可选)
# 返回: 0-通过, 65-类型错误
# 说明: 检查输入值是否为有效JSON对象格式
function utils_validate_object() {
    local value="$1"
    local name="${2:-参数}"
    
    if [[ -z "$value" ]]; then
        printf "%s\n" "错误: ${name}不能为空" >&2
        return $UTILS_VALIDATE_ERROR_REQUIRED
    fi
    
    # 检查是否为有效的JSON对象格式
    if [[ "$value" =~ ^\{.*\}$ ]]; then
        # 如果有jq工具，进行更严格的JSON验证
        if command -v jq >/dev/null 2>&1; then
            if echo "$value" | jq empty >/dev/null 2>&1; then
                return 0
            else
                printf "%s\n" "错误: ${name}不是有效的JSON对象格式: ${value}" >&2
                return $UTILS_VALIDATE_ERROR_TYPE
            fi
        else
            return 0
        fi
    else
        printf "%s\n" "错误: ${name}不是有效的对象格式: ${value}" >&2
        return $UTILS_VALIDATE_ERROR_TYPE
    fi
}

###############################################################################
# 特殊格式校验函数
###############################################################################

# 函数: 校验邮箱地址
# 参数: $1 - 输入值; $2 - 参数名称(可选)
# 返回: 0-通过, 66-格式错误
# 说明: 检查输入值是否为有效邮箱地址格式
function utils_validate_email() {
    local value="$1"
    local name="${2:-邮箱地址}"
    
    if [[ -z "$value" ]]; then
        printf "%s\n" "错误: ${name}不能为空" >&2
        return $UTILS_VALIDATE_ERROR_REQUIRED
    fi
    
    # 邮箱地址正则表达式
    local email_regex='^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
    
    if [[ ! "$value" =~ $email_regex ]]; then
        printf "%s\n" "错误: ${name}格式不正确: ${value}" >&2
        return $UTILS_VALIDATE_ERROR_FORMAT
    fi
    
    return 0
}

# 函数: 校验URL地址
# 参数: $1 - 输入值; $2 - 参数名称(可选)
# 返回: 0-通过, 66-格式错误
# 说明: 检查输入值是否为有效URL地址格式
function utils_validate_url() {
    local value="$1"
    local name="${2:-URL地址}"
    
    if [[ -z "$value" ]]; then
        printf "%s\n" "错误: ${name}不能为空" >&2
        return $UTILS_VALIDATE_ERROR_REQUIRED
    fi
    
    # URL地址正则表达式
    local url_regex='^https?://[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}(/.*)?$'
    
    if [[ ! "$value" =~ $url_regex ]]; then
        printf "%s\n" "错误: ${name}格式不正确: ${value}" >&2
        return $UTILS_VALIDATE_ERROR_FORMAT
    fi
    
    return 0
}

# 函数: 校验IP地址
# 参数: $1 - 输入值; $2 - 参数名称(可选)
# 返回: 0-通过, 66-格式错误
# 说明: 检查输入值是否为有效IP地址格式(IPv4)
function utils_validate_ip() {
    local value="$1"
    local name="${2:-IP地址}"
    
    if [[ -z "$value" ]]; then
        printf "%s\n" "错误: ${name}不能为空" >&2
        return $UTILS_VALIDATE_ERROR_REQUIRED
    fi
    
    # IPv4地址正则表达式
    local ip_regex='^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$'
    
    if [[ ! "$value" =~ $ip_regex ]]; then
        printf "%s\n" "错误: ${name}格式不正确: ${value}" >&2
        return $UTILS_VALIDATE_ERROR_FORMAT
    fi
    
    return 0
}

# 函数: 校验MAC地址
# 参数: $1 - 输入值; $2 - 参数名称(可选)
# 返回: 0-通过, 66-格式错误
# 说明: 检查输入值是否为有效MAC地址格式
function utils_validate_mac() {
    local value="$1"
    local name="${2:-MAC地址}"
    
    if [[ -z "$value" ]]; then
        printf "%s\n" "错误: ${name}不能为空" >&2
        return $UTILS_VALIDATE_ERROR_REQUIRED
    fi
    
    # MAC地址正则表达式（支持多种分隔符）
    local mac_regex='^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$'
    
    if [[ ! "$value" =~ $mac_regex ]]; then
        printf "%s\n" "错误: ${name}格式不正确: ${value}" >&2
        return $UTILS_VALIDATE_ERROR_FORMAT
    fi
    
    return 0
}

# 函数: 校验电话号码
# 参数: $1 - 输入值; $2 - 参数名称(可选)
# 返回: 0-通过, 66-格式错误
# 说明: 检查输入值是否为有效电话号码格式(支持多种格式)
function utils_validate_phone() {
    local value="$1"
    local name="${2:-电话号码}"
    
    if [[ -z "$value" ]]; then
        printf "%s\n" "错误: ${name}不能为空" >&2
        return $UTILS_VALIDATE_ERROR_REQUIRED
    fi
    
    # 移除所有非数字字符
    local clean_value=$(echo "$value" | tr -d '[:space:]-()')
    
    # 检查是否为纯数字且长度在7-15位之间
    if [[ "$clean_value" =~ ^[0-9]{7,15}$ ]]; then
        return 0
    else
        printf "%s\n" "错误: ${name}格式不正确: ${value}" >&2
        return $UTILS_VALIDATE_ERROR_FORMAT
    fi
}

# 函数: 校验日期格式
# 参数: $1 - 输入值; $2 - 日期格式(可选，默认YYYY-MM-DD); $3 - 参数名称(可选)
# 返回: 0-通过, 66-格式错误
# 说明: 检查输入值是否为有效日期格式
function utils_validate_date() {
    local value="$1"
    local format="${2:-%Y-%m-%d}"
    local name="${3:-日期}"
    
    if [[ -z "$value" ]]; then
        printf "%s\n" "错误: ${name}不能为空" >&2
        return $UTILS_VALIDATE_ERROR_REQUIRED
    fi
    
    # 使用date命令验证日期格式
    if date -d "$value" >/dev/null 2>&1; then
        return 0
    else
        printf "%s\n" "错误: ${name}格式不正确: ${value}" >&2
        return $UTILS_VALIDATE_ERROR_FORMAT
    fi
}

# 函数: 校验时间格式
# 参数: $1 - 输入值; $2 - 时间格式(可选，默认HH:MM:SS); $3 - 参数名称(可选)
# 返回: 0-通过, 66-格式错误
# 说明: 检查输入值是否为有效时间格式
function utils_validate_time() {
    local value="$1"
    local format="${2:-%H:%M:%S}"
    local name="${3:-时间}"
    
    if [[ -z "$value" ]]; then
        printf "%s\n" "错误: ${name}不能为空" >&2
        return $UTILS_VALIDATE_ERROR_REQUIRED
    fi
    
    # 时间格式正则表达式
    local time_regex='^([01]?[0-9]|2[0-3]):[0-5][0-9](:[0-5][0-9])?$'
    
    if [[ "$value" =~ $time_regex ]]; then
        return 0
    else
        printf "%s\n" "错误: ${name}格式不正确: ${value}" >&2
        return $UTILS_VALIDATE_ERROR_FORMAT
    fi
}

###############################################################################
# 配置内容校验函数
###############################################################################

# 函数: 校验必填字段
# 参数: $1 - 字段值; $2 - 字段名; $3 - 配置文件名(可选)
# 返回: 0-通过, 64-必填字段缺失
# 说明: 检查配置文件中必填字段是否存在且非空
function utils_validate_required_field() {
    local value="$1"
    local field_name="$2"
    local config_file="${3:-配置文件}"
    
    if [[ -z "$value" ]]; then
        printf "%s\n" "错误: ${config_file}中必填字段缺失: ${field_name}" >&2
        return $UTILS_VALIDATE_ERROR_REQUIRED
    fi
    
    return 0
}

# 函数: 校验字段类型
# 参数: $1 - 字段值; $2 - 期望类型; $3 - 字段名; $4 - 配置文件名(可选)
# 返回: 0-通过, 65-类型错误
# 说明: 检查配置字段的类型是否符合期望
function utils_validate_field_type() {
    local value="$1"
    local expected_type="$2"
    local field_name="$3"
    local config_file="${4:-配置文件}"
    
    case "$expected_type" in
        "integer")
            utils_validate_integer "$value" "$field_name" || return $?
            ;;
        "float")
            utils_validate_float "$value" "$field_name" || return $?
            ;;
        "boolean")
            utils_validate_boolean "$value" "$field_name" || return $?
            ;;
        "string")
            utils_validate_string "$value" 0 "" "$field_name" || return $?
            ;;
        "email")
            utils_validate_email "$value" "$field_name" || return $?
            ;;
        "url")
            utils_validate_url "$value" "$field_name" || return $?
            ;;
        "ip")
            utils_validate_ip "$value" "$field_name" || return $?
            ;;
        *)
            printf "%s\n" "错误: ${config_file}中字段${field_name}类型不支持: ${expected_type}" >&2
            return $UTILS_VALIDATE_ERROR_TYPE
            ;;
    esac
    
    return 0
}

# 函数: 校验字段值范围
# 参数: $1 - 字段值; $2 - 最小值; $3 - 最大值; $4 - 字段名; $5 - 配置文件名(可选)
# 返回: 0-通过, 67-范围错误
# 说明: 检查数值字段是否在指定范围内
function utils_validate_field_range() {
    local value="$1"
    local min_value="$2"
    local max_value="$3"
    local field_name="$4"
    local config_file="${5:-配置文件}"
    
    # 首先验证是否为数字
    if ! utils_validate_number "$value" "$field_name" 2>/dev/null; then
        return $UTILS_VALIDATE_ERROR_TYPE
    fi
    
    # 检查范围
    if [[ $(echo "$value < $min_value" | bc -l 2>/dev/null || echo "0") -eq 1 ]]; then
        printf "%s\n" "错误: ${config_file}中字段${field_name}值不能小于${min_value}，当前值: ${value}" >&2
        return $UTILS_VALIDATE_ERROR_RANGE
    fi
    
    if [[ $(echo "$value > $max_value" | bc -l 2>/dev/null || echo "0") -eq 1 ]]; then
        printf "%s\n" "错误: ${config_file}中字段${field_name}值不能大于${max_value}，当前值: ${value}" >&2
        return $UTILS_VALIDATE_ERROR_RANGE
    fi
    
    return 0
}

# 函数: 校验一致性
# 参数: $1 - 值1; $2 - 值2; $3 - 字段名1; $4 - 字段名2; $5 - 配置文件名(可选)
# 返回: 0-通过, 68-一致性错误
# 说明: 检查两个字段值是否一致
function utils_validate_consistency() {
    local value1="$1"
    local value2="$2"
    local field1="$3"
    local field2="$4"
    local config_file="${5:-配置文件}"
    
    if [[ "$value1" != "$value2" ]]; then
        printf "%s\n" "错误: ${config_file}中字段${field1}和${field2}不一致" >&2
        return $UTILS_VALIDATE_ERROR_DEPENDENCY
    fi
    
    return 0
}

# 函数: 校验依赖关系
# 参数: $1 - 依赖字段值; $2 - 被依赖字段值; $3 - 依赖字段名; $4 - 被依赖字段名; $5 - 配置文件名(可选)
# 返回: 0-通过, 68-依赖关系错误
# 说明: 检查字段间的依赖关系是否满足
function utils_validate_dependency() {
    local dependent_value="$1"
    local dependency_value="$2"
    local dependent_field="$3"
    local dependency_field="$4"
    local config_file="${5:-配置文件}"
    
    # 如果依赖字段为空，则被依赖字段也必须为空
    if [[ -z "$dependency_value" && -n "$dependent_value" ]]; then
        printf "%s\n" "错误: ${config_file}中字段${dependent_field}依赖于${dependency_field}，但${dependency_field}为空" >&2
        return $UTILS_VALIDATE_ERROR_DEPENDENCY
    fi
    
    return 0
}

###############################################################################
# 安全策略校验函数
###############################################################################

# 函数: 校验密码强度
# 参数: $1 - 密码; $2 - 最小长度(可选，默认8); $3 - 参数名称(可选)
# 返回: 0-通过, 67-强度不足
# 说明: 检查密码强度是否符合安全要求
function utils_validate_password_strength() {
    local password="$1"
    local min_length="${2:-8}"
    local name="${3:-密码}"
    
    if [[ -z "$password" ]]; then
        printf "%s\n" "错误: ${name}不能为空" >&2
        return $UTILS_VALIDATE_ERROR_REQUIRED
    fi
    
    local length=${#password}
    local has_upper=false
    local has_lower=false
    local has_digit=false
    local has_special=false
    
    # 检查长度
    if [[ $length -lt $min_length ]]; then
        printf "%s\n" "错误: ${name}长度不能少于${min_length}个字符，当前长度: ${length}" >&2
        return $UTILS_VALIDATE_ERROR_RANGE
    fi
    
    # 检查字符类型
    if [[ "$password" =~ [A-Z] ]]; then
        has_upper=true
    fi
    
    if [[ "$password" =~ [a-z] ]]; then
        has_lower=true
    fi
    
    if [[ "$password" =~ [0-9] ]]; then
        has_digit=true
    fi
    
    if [[ "$password" =~ [^A-Za-z0-9] ]]; then
        has_special=true
    fi
    
    # 检查强度要求
    local strength_issues=()
    
    if [[ "$has_upper" == false ]]; then
        strength_issues+=("缺少大写字母")
    fi
    
    if [[ "$has_lower" == false ]]; then
        strength_issues+=("缺少小写字母")
    fi
    
    if [[ "$has_digit" == false ]]; then
        strength_issues+=("缺少数字")
    fi
    
    if [[ "$has_special" == false ]]; then
        strength_issues+=("缺少特殊字符")
    fi
    
    if [[ ${#strength_issues[@]} -gt 0 ]]; then
        printf "%s\n" "错误: ${name}强度不足，需要包含: ${strength_issues[*]}" >&2
        return $UTILS_VALIDATE_ERROR_RANGE
    fi
    
    return 0
}

###############################################################################
# 辅助函数
###############################################################################

# 函数: 校验数字(整数或浮点数)
# 参数: $1 - 输入值; $2 - 参数名称(可选)
# 返回: 0-通过, 65-类型错误
# 说明: 检查输入值是否为有效数字(整数或浮点数)
function utils_validate_number() {
    local value="$1"
    local name="${2:-参数}"
    
    if [[ -z "$value" ]]; then
        printf "%s\n" "错误: ${name}不能为空" >&2
        return $UTILS_VALIDATE_ERROR_REQUIRED
    fi
    
    if [[ ! "$value" =~ ^-?[0-9]+(\.[0-9]+)?$ ]]; then
        printf "%s\n" "错误: ${name}不是有效的数字: ${value}" >&2
        return $UTILS_VALIDATE_ERROR_TYPE
    fi
    
    return 0
}
